/* Copyright Thanh-Trung Pham, 2015 */
package jp.ac.uaizu.generation;

import jp.ac.uaizu.message.SerCompGeneratorsException;
import jp.ac.uaizu.message.SerCompMessageResource;
import jp.ac.uaizu.message.SerCompMessageUtility;

import java.io.File;
import java.io.FilenameFilter;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;

/**
 * @author Thanh-Trung Pham
 */
public class SerCompGenerators {
    private ClassLoader m_oClassLoader;
    private String m_strGeneratorsFolderName;

    /**
     * The constructor.
     *
     * @param generatorsFolderName the generators' folder name.
     * @throws SerCompGeneratorsException if there is an error.
     */
    public SerCompGenerators(String generatorsFolderName) throws SerCompGeneratorsException {
        m_strGeneratorsFolderName = generatorsFolderName;
        initialize();
    }


    /**
     * Initializer.
     *
     * @throws SerCompGeneratorsException if there is an error.
     */
    private void initialize() throws SerCompGeneratorsException {
        File oGeneratorsFolder = new File(m_strGeneratorsFolderName);

        if (!oGeneratorsFolder.exists()) {
            throw new SerCompGeneratorsException(SerCompMessageUtility.getResourceMessage(SerCompMessageResource.GENERATORS_FOLDER_NOT_EXISTING, m_strGeneratorsFolderName));
        }

        if(oGeneratorsFolder.isFile()){
            throw new SerCompGeneratorsException(SerCompMessageUtility.getResourceMessage(SerCompMessageResource.GENERATORS_FOLDER_EXPECTED, m_strGeneratorsFolderName));
        }

    }

    /**
     * Prepare the class loader.
     *
     * @throws SerCompGeneratorsException if there is an error.
     */
    private void prepareClassLoader() throws SerCompGeneratorsException {
        File oGeneratorsFolder = new File(m_strGeneratorsFolderName);

        FilenameFilter oFilter = new FilenameFilter() {
            public boolean accept(File file, String name) {
                return name.endsWith(".jar");
            }
        };

        File[] oJarFiles = oGeneratorsFolder.listFiles(oFilter);
        URL[] oURLs = new URL[oJarFiles.length + 1];

        try {
            oURLs[0] = oGeneratorsFolder.toURI().toURL();
            for (int i = 0; i < oJarFiles.length; i++) {
                oURLs[i + 1] = oJarFiles[i].toURI().toURL();
            }
        } catch (MalformedURLException oEx) {
            throw new SerCompGeneratorsException(SerCompMessageUtility.getResourceMessage(SerCompMessageResource.GENERATORS_UNEXPECTED_EXCEPTION, oEx.getMessage()));
        }

        m_oClassLoader = new URLClassLoader(oURLs);
    }

    /**
     * Check the requirements for a generator.
     *
     * @param generatorName  the generator's name.
     * @param generatorClass the generator's class.
     * @throws SerCompGeneratorsException if there is an error.
     */
    private void checkRequirements(String generatorName, Class generatorClass) throws SerCompGeneratorsException {
        if (generatorClass.isInterface()) {
            throw new SerCompGeneratorsException(SerCompMessageUtility.getResourceMessage(SerCompMessageResource.GENERATORS_NOT_CLASS));
        }

        Class[] oInterfaces = generatorClass.getInterfaces();

        boolean isImplementedGenerationInterface = false;
        for (Class oInterface : oInterfaces) {
            if (oInterface.getName().equals(ISerCompGenerator.class.getName())) {
                isImplementedGenerationInterface = true;
            }
        }

        if (!isImplementedGenerationInterface) {
            throw new SerCompGeneratorsException(SerCompMessageUtility.getResourceMessage(SerCompMessageResource.GENERATORS_MISSING_INTERFACE, generatorName, ISerCompGenerator.class.getName()));
        }
    }


    /**
     * Get a registered generator by name.
     *
     * @param generatorName the name of the generator.
     * @return the registered generator.
     * @throws SerCompGeneratorsException if there is an error.
     */
    public ISerCompGenerator getGenerator(String generatorName) throws SerCompGeneratorsException {
        prepareClassLoader();

        try {
            Class oGeneratorClass = m_oClassLoader.loadClass(generatorName);
            checkRequirements(generatorName, oGeneratorClass);

            return (ISerCompGenerator) oGeneratorClass.newInstance();
        } catch (ClassNotFoundException oClassNotFoundException) {
            throw new SerCompGeneratorsException(SerCompMessageUtility.getResourceMessage(SerCompMessageResource.GENERATORS_NOT_FOUND, generatorName, m_strGeneratorsFolderName));
        } catch (InstantiationException oInstantiationException) {
            throw new SerCompGeneratorsException(SerCompMessageUtility.getResourceMessage(SerCompMessageResource.GENERATORS_UNEXPECTED_EXCEPTION, oInstantiationException.getMessage()));
        } catch (IllegalAccessException oIllegalAccessException) {
            throw new SerCompGeneratorsException(SerCompMessageUtility.getResourceMessage(SerCompMessageResource.GENERATORS_UNEXPECTED_EXCEPTION, oIllegalAccessException.getMessage()));
        }
    }
}
